package com.chinastock.log;

import com.alibaba.fastjson.JSON;
import com.chinastock.communication.BaseResponse;
import com.chinastock.model.ContentType;
import com.chinastock.model.EcpMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.support.WebExchangeBindException;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * @author zhangfan.it
 * @date 2021/1/22
 */
@Component
@Order(Filters.EXCEPTION_HANDLER)
public class ExceptionHandler implements ErrorWebExceptionHandler {
    private static final Logger log = LoggerFactory.getLogger(LogName.ECP);

    @Value("${response.log.switch:true}")
    private boolean isOn;

    @Value("${response.log.excludes:}")
    private String excludes;

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        EcpMessage ecpMessage = new EcpMessage();
        ecpMessage.setUrl(exchange.getRequest().getURI().getPath());
        ecpMessage.setException(ex);
        log.error(ecpMessage.toString());

        MediaType type = exchange.getRequest().getHeaders().getContentType();
        String typeStr = type == null ? "UnknownType" : type.getSubtype();

        byte[] bytes;
        if (ContentType.JSON.getType().toLowerCase().equals(typeStr.toLowerCase())) {
            BaseResponse<Void> ret = new BaseResponse<>(false, "501", "exp");
            if (ex instanceof WebExchangeBindException) {
                WebExchangeBindException weEx = (WebExchangeBindException)ex;
                StringBuilder error = new StringBuilder();
                List<FieldError> fieldErrors = weEx.getFieldErrors();
                if (!CollectionUtils.isEmpty(fieldErrors)) {
                    for (FieldError fieldError : fieldErrors) {
                        error.append(fieldError.getField()).append(fieldError.getDefaultMessage()).append(";");
                    }
                } else {
                    for (ObjectError e : ((WebExchangeBindException) ex).getBindingResult().getAllErrors()) {
                        error.append(e.getDefaultMessage()).append(";");
                        String[] codes = e.getCodes();
                        if (codes != null) {
                            for (String code : codes) {
                                error.append(code).append(";");
                            }
                        }
                    }
                }
                ret.getMeta().setMessage(error.toString());
                ret.getMeta().setCode(weEx.getStatus().value()+"");
            } else if (ex instanceof WebClientResponseException) {
                ret.getMeta().setMessage(ex.getMessage());

                WebClientResponseException weEx = (WebClientResponseException)ex;
                ret.getMeta().setCode(weEx.getStatusCode().value()+"");
            } else {
                ret.getMeta().setMessage(ex.getMessage());
            }

            String jsonStr = JSON.toJSONString(ret);
            exchange.getResponse().setStatusCode(HttpStatus.OK);
            bytes = jsonStr.getBytes(StandardCharsets.UTF_8);
        } else {
            BaseResponse<Void> ret = new BaseResponse<>(false, "501", "exp");
            ret.getMeta().setMessage(ex.getMessage());
            String jsonStr = JSON.toJSONString(ret);
            exchange.getResponse().setStatusCode(HttpStatus.OK);
            bytes = jsonStr.getBytes(StandardCharsets.UTF_8);
        }

        DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
        return new ExtractResponseBodyServerWebExchangeDecorator(exchange, excludes, isOn).getResponse().writeWith(Mono.just(buffer));
    }
}
